import Reflect from "@/utils/reflect";
import { AnyAsyncFunc } from "./type";

export function Loading(phase?: string) {
  return function<
    T extends { [key in K]: boolean },
    K extends string,
  > (target: T, propertyKey: K) {
    Reflect.pushValue(target, ["loading", phase || ""], propertyKey);
  }
}

export function LoadingIntercept(...phases: string[]) {
  phases = phases.length === 0 ? [""] : phases;
  return function(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<AnyAsyncFunc>) {
    Reflect.setValue(target, ["LoadingIntercept", propertyKey], descriptor.value);
    descriptor.value = async function(...args) {
      var instance: any = this;
      try {
        for (const phase of phases) {
          var loadPropertys: string[] = Reflect.getValue(target, ["loading", phase], []);
          for (const loadProperty of loadPropertys) {
            instance[loadProperty] = true;
            var loadingCount = Reflect.getValue(instance, ["loadingCount", loadProperty], 0);
            Reflect.setValue(instance, ["loadingCount", loadProperty], loadingCount + 1);
          }
        }
        await Reflect.getValue<AnyAsyncFunc>(target, ["LoadingIntercept", propertyKey]).call(this, ...args);
      } finally {
        for (const phase of phases) {
          var loadPropertys: string[] = Reflect.getValue(target, ["loading", phase], []);
          for (const loadProperty of loadPropertys) {
            var loadingCount = Reflect.getValue(instance, ["loadingCount", loadProperty], 0);
            Reflect.setValue(instance, ["loadingCount", loadProperty], loadingCount - 1);
            if (loadingCount - 1 === 0) {
              instance[loadProperty] = false;
            }
          }
        }
      }
    }
  }
}
